#include <stdlib.h>
#include <stdio.h>
#include "galois.h"
#include "galois_add.h"
#include <map>
using namespace std;

main (int argc, char **argv)
{
  int total;
  int i, j, k;
  int l, n, s, t;
  int product, mask1, mask0, mask2, mask3;
  int a4, a5, a6;
  int sa4, ta4, sa5, ta5, sa6, ta6;
  int prod1, prod2;
  map <int, int> counts;
  map <int, int>::iterator cit;

  if (argc != 2) {
    fprintf(stderr, "usage: program w\n");
    exit(1);
  }

  n = atoi(argv[1]);
  if (n <= 0 || n % 4 != 0) {
    fprintf(stderr, "w must be a positive multiple of 4\n");
    exit(1);
  }

  l = n/4;
  s = 2;
  t = 3;

  mask0 = (1 << l) - 1;
  mask1 = mask0 << l;
  mask2 = mask1 << l;
  mask3 = mask2 << l;

  for (i = 1; i < (1 << n); i++) {
    for (j = 0; j < (1 << n); j++) {
      a4  = galois_single_multiply((i & mask1) >> (l * 1), (j & mask3) >> (l * 3), l);
      a4 ^= galois_single_multiply((i & mask2) >> (l * 2), (j & mask2) >> (l * 2), l);
      a4 ^= galois_single_multiply((i & mask3) >> (l * 3), (j & mask1) >> (l * 1), l);

      a5  = galois_single_multiply((i & mask3) >> (l * 3), (j & mask2) >> (l * 2), l);
      a5 ^= galois_single_multiply((i & mask2) >> (l * 2), (j & mask3) >> (l * 3), l);

      a6  = galois_single_multiply((i & mask3) >> (l * 3), (j & mask3) >> (l * 3), l);

      sa4 = galois_single_multiply(s, a4, l);
      ta4 = galois_single_multiply(t, a4, l);
      sa5 = galois_single_multiply(s, a5, l);
      ta5 = galois_single_multiply(t, a5, l);
      sa6 = galois_single_multiply(s, a6, l);
      ta6 = galois_single_multiply(t, a6, l);

      /* x ^ 3 terms */
      product  = galois_single_multiply( i          >> (l * 3), (j & mask0),            l);
      product ^= galois_single_multiply((i & mask2) >> (l * 2), (j & mask1) >> (l * 1), l);
      product ^= galois_single_multiply((i & mask1) >> (l * 1), (j & mask2) >> (l * 2), l);
      product ^= galois_single_multiply((i & mask0),             j          >> (l * 3), l);

      product ^= a5;
      product ^= sa6;
      product <<= l;

      /* x ^ 2 terms */
      product ^= galois_single_multiply((i & mask2) >> (l * 2), (j & mask0), l);
      product ^= galois_single_multiply((i & mask1) >> (l * 1), (j & mask1) >> (l * 1), l);
      product ^= galois_single_multiply((i & mask0),            (j & mask2) >> (l * 2), l);

      product ^= a4;
      product ^= sa5;
      product ^= ta6;
      product ^= a6;
      product <<= l;

      /* x ^ 1 terms */
      product ^= galois_single_multiply((i & mask1) >> (l * 1), (j & mask0), l);
      product ^= galois_single_multiply((i & mask0),            (j & mask1) >> (l * 1), l);
      product ^= sa4;
      product ^= ta5;
      product ^= sa6;
      product <<= l;

      /* x ^ 0 terms */
      product ^= galois_single_multiply((i & mask0),            (j & mask0), l);
      product ^= ta4;
      product ^= ta6;

      if (i == 5 && j == 13) printf("%d\n", product);
      if (product == 1) {
        printf("%d and %d\n", i, j);
        printf("%d and %d\n", i, galois_composite_inverse_k4(i, n, s, t));
	printf("\n");
      }
      cit = counts.find(product);
      if (cit != counts.end()) {
        cit->second++;
				printf("error\n");
        counts.clear();
        break;
      } else {
        counts.insert(make_pair(product, 1));
      }
    }
	  counts.clear();
  }
}
